package net.xo68.destiny.starter.worker.logger.impl;

import net.xo68.destiny.core.util.TimeUtils;
import net.xo68.destiny.starter.worker.logger.JobLogger;
import net.xo68.destiny.starter.worker.logger.JobLoggingEvent;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.helpers.FormattingTuple;
import org.slf4j.helpers.MessageFormatter;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Queue;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 日志实现
 * @author wuxie
 * @version 2020-10-28
 */
public class JobLoggerImpl implements JobLogger {

    private final long loggerIndex;
    private final Queue<JobLoggingEvent> eventQueue;

    private final AtomicLong lastTimeStamp=new AtomicLong(0L);

    public JobLoggerImpl(long loggerIndex,Queue<JobLoggingEvent> eventQueue) {
        this.loggerIndex = loggerIndex;
        this.eventQueue=eventQueue;
    }

    @Override
    public long loggerIndex() {
        return this.loggerIndex;
    }

    @Override
    public long getLastTimeStamp() {
        return lastTimeStamp.get();
    }

    @Override
    public void setLastTimeStamp(long lastTimeStamp) {
        this.lastTimeStamp.set(lastTimeStamp);
    }

    @Override
    public void log(String format, Object arg) {
        recordEvent_1Args(true,format,arg);
    }
    @Override
    public void log(boolean decorate, String format, Object arg) {
        recordEvent_1Args(decorate,format,arg);
    }
    @Override
    public void log(String format, Object arg0, Object arg1) {
        recordEvent2Args(true,format,arg0,arg1);
    }
    @Override
    public void log(boolean decorate, String format, Object arg0, Object arg1) {
        recordEvent2Args(decorate,format,arg0,arg1);
    }

    @Override
    public void log(String format, Object... args) {
        recordEventArgArray(true,format,args);
    }
    @Override
    public void log(boolean decorate,String format,  Object... args) {
        recordEventArgArray(decorate,format,args);
    }

    @Override
    public void log(boolean decorate,String msg,Throwable t) {
        recordEvent_0Args(decorate,msg,t);
    }
    @Override
    public void log(String msg, Throwable t) {
        recordEvent_0Args(true,msg,t);
    }



    private void recordEvent_0Args(boolean decorate, String msg, Throwable t) {
        recordEvent(decorate, msg, null, t);
    }

    private void recordEvent_1Args(boolean decorate, String msg, Object arg1) {
        recordEvent(decorate, msg, new Object[] { arg1 }, null);
    }

    private void recordEvent2Args(boolean decorate, String msg, Object arg1, Object arg2) {
        if (arg2 instanceof Throwable) {
            recordEvent(decorate, msg, new Object[] { arg1 }, (Throwable) arg2);
        } else {
            recordEvent(decorate, msg, new Object[] { arg1, arg2 }, null);
        }
    }

    private void recordEventArgArray(boolean decorate,String msg, Object[] args){
        Throwable throwableCandidate = MessageFormatter.getThrowableCandidate(args);
        if (throwableCandidate != null) {
            Object[] trimmedCopy = MessageFormatter.trimmedCopy(args);
            recordEvent(decorate, msg, trimmedCopy, throwableCandidate);
        } else {
            recordEvent(decorate, msg, args, null);
        }
    }
    /**
     * 记录日志事件
     * @param decorate
     * @param msg
     * @param args
     * @param throwable
     */
    private void recordEvent(boolean decorate,String msg, Object[] args, Throwable throwable){
        StringBuilder msgSb=new StringBuilder();
        if(StringUtils.isNotEmpty(msg) && args!=null){
            FormattingTuple ft = MessageFormatter.arrayFormat(msg, args);
            msgSb.append(ft.getMessage());
        }else {
            msgSb.append(msg);
        }
        if(throwable!=null){
            msgSb.append("\n");
            StringWriter stringWriter = new StringWriter();
            throwable.printStackTrace(new PrintWriter(stringWriter));
            msgSb.append(stringWriter.toString());
        }

        long current= TimeUtils.nowMilli();
        this.setLastTimeStamp(current);
        JobLoggingEvent jobLoggingEvent=new JobLoggingEvent();
        jobLoggingEvent.setLoggerIndex(this.loggerIndex);
        jobLoggingEvent.setDecorate(decorate);
        jobLoggingEvent.setJobLogger(this);
        jobLoggingEvent.setMessage(msgSb.toString());
        jobLoggingEvent.setTimeStamp(current);

        StackTraceElement callInfo = new Throwable().getStackTrace()[1];
        jobLoggingEvent.setClassName(callInfo.getFileName().replace(".java", "") );
        jobLoggingEvent.setMethodName(callInfo.getMethodName());
        jobLoggingEvent.setLineName(callInfo.getLineNumber());
        jobLoggingEvent.setThreadName(Thread.currentThread().getName());

        eventQueue.add(jobLoggingEvent);
    }
}
